; Memory API Handler (INT 21h)
; AH - Operation
;   00 - Reset buffer
;    SI - buffer
;    CX - length
;   01 - Copy memory from A to B
;    SI - A
;    BX - B
;    CX - Length
;   02 - Compare strings
;    SI - A
;    BX - B
;    ---
;    CF if strings not equal
;   03 - Copy string
;    SI - A
;    BX - B
;   04 - Count len of string
;    SI - Pointer on string
;    ---
;    AX - Len of string
;   05 - Convert ascii buffer to int
;    SI - Pointer on string
;    ---
;    AX - Number
;   06 - Convert int to ascii buffer
;    SI - Pointer on buffer
;    AX - Number
;    ---
;    SI - Pointer on buffer

ext_memory_handler:
    pusha

    cmp ah, 0
    je .reset_buffer

    cmp ah, 1
    je .copy_memory

    cmp ah, 2
    je .compare_strings

    cmp ah, 3
    je .copy_string

    cmp ah, 4
    je .calculate_string_len

    cmp ah, 5
    je .string_to_int

    cmp ah, 6
    je .int_to_string

    jmp .end_nocom

.reset_buffer:
    call reset_buffer
    jmp .end

.copy_memory:
    call copy_memory
    jmp .end

.compare_strings:
    call compare_strings
    jc .compare_strings.no
    popa
    clc
    iret
.compare_strings.no:
    popa
    stc
    iret

.copy_string:
    call copy_string
    jmp .end

.calculate_string_len:
    call calculate_string_len
    mov word [.ax], ax
    popa
    mov ax, word [.ax]
    iret

.string_to_int:
    call string_to_int
    mov word [.ax], ax
    popa
    mov ax, word [.ax]
    iret

.int_to_string:
    call int_to_string
    mov word [.si], si
    popa
    mov si, word [.si]
    iret
.end:
    popa
    iret
.end_nocom:
    popa
    iret
    .ax dw 0
    .si dw 0

; Reset buffer
; DS:SI - buffer
; CX - Length
reset_buffer:
    push si
    push cx
reset_buffer.lp:
    mov byte [ds:si], 0
    inc si

    dec cx
    cmp cx, 0
    jne reset_buffer.lp
reset_buffer.nd:
    pop cx
    pop si
    ret


; Copy memory from A to B
; SI - A
; BX - B
; CX - Number of bytes to copy
copy_memory:
    pusha
.loop:
    lodsb
    mov [bx], al
    inc bx
    loop .loop
.end:
    popa
    ret

; Compare strings
;
; DS:SI - Pointer on first string
; DS:BX - Pointer on second string
; ---
; Carry flag - 1 if strings are not equal
compare_strings:
    pusha
compare_strings.comp:
    lodsb

    cmp [bx], al
    jne compare_strings.not_equal
    cmp al, 0
    je compare_strings.equal

    inc bx

    jmp compare_strings.comp
compare_strings.equal:
    clc
    jmp compare_strings.return
compare_strings.not_equal:
    stc
    jmp compare_strings.return
compare_strings.return:
    popa
    ret

; Copies string
;
; SI - Pointer on memory from where copy
; BX - Pointer on memory where copy
copy_string:
    pusha
copy_string.lp:
    lodsb

    cmp al, 0
    je copy_string.ed

    mov byte [bx], al

    inc bx

    jmp copy_string.lp
copy_string.ed:
    popa
    ret

; Calculate len of string
;
; SI - Pointer on string
; ---
; AX - Len of string
calculate_string_len:
    push si
    xor ax, ax
    push ax
calculate_string_len.lp:
    lodsb
    cmp al, 0
    je calculate_string_len.ed

    pop ax
    inc ax
    push ax

    jmp calculate_string_len.lp
calculate_string_len.ed:
    pop ax
    pop si
    ret

; Convert ascii buffer to int
;
; SI - Pointer on string
; ---
; AX - Number
string_to_int:
    pusha

    call calculate_string_len

    add si, ax          ; Work from rightmost char in string
    dec si

    mov cx, ax          ; Use string length as counter

    mov bx, 0           ; BX will be the final number
    mov ax, 0


    ; As we move left in the string, each char is a bigger multiple. The
    ; right-most character is a multiple of 1, then next (a char to the
    ; left) a multiple of 10, then 100, then 1,000, and the final (and
    ; leftmost char) in a five-char number would be a multiple of 10,000

    mov word [.multiplier], 1   ; Start with multiples of 1

.loop:
    mov ax, 0
    mov byte al, [si]       ; Get character
    sub al, 48          ; Convert from ASCII to real number

    mul word [.multiplier]      ; Multiply by our multiplier

    add bx, ax          ; Add it to BX

    push ax             ; Multiply our multiplier by 10 for next char
    mov word ax, [.multiplier]
    mov dx, 10
    mul dx
    mov word [.multiplier], ax
    pop ax

    dec cx              ; Any more chars?
    cmp cx, 0
    je .finish
    dec si              ; Move back a char in the string
    jmp .loop

.finish:
    mov word [.tmp], bx
    popa
    mov word ax, [.tmp]

    ret


    .multiplier dw 0
    .tmp        dw 0


; Convert unsigned int to string
;
; AX - Num to convert
; SI - Where save string
; ---
; SI - Pointer to string with converted num
int_to_string:
    push cx
    push bx
    push di
    push ax

    mov cx, 0
    mov bx, 10          ; Set BX 10, for division and mod
    mov di, si         ; Get our pointer ready

int_to_string.push:
    mov dx, 0
    div bx              ; Remainder in DX, quotient in AX
    inc cx              ; Increase pop loop counter
    push dx             ; Push remainder, so as to reverse order when popping
    test ax, ax         ; Is quotient zero?
    jnz int_to_string.push           ; If not, loop again
int_to_string.pop:
    pop dx              ; Pop off values in reverse order, and add 48 to make them digits
    add dl, '0'         ; And save them in the string, increasing the pointer each time
    mov [di], dl
    inc di
    dec cx
    jnz int_to_string.pop

    mov byte [di], 0        ; Zero-terminate string

    mov si, di
    pop ax
    pop di
    pop bx
    pop cx
    ret
